/**
 * 
 */
package kit4j.util.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import kit4j.util.lang.StringUtil;

/**
 * 类型对象操作工具类。
 * 
 * @author MiniKnife
 * 
 */
public abstract class ClassUtil {

	private static class SingletonHolder {
		protected static Map<Class<?>, Set<Class<?>>> completedSetMap;

		static {
			completedSetMap = new HashMap<Class<?>, Set<Class<?>>>();
		}
	}

	/**
	 * 获取给定类型的所有父类和实现的接口类型集合。
	 * 
	 * @param clazz
	 *            类型对象
	 * @return 给定类型的所有父类和实现的接口类型集合。
	 */
	public static Set<Class<?>> getSuperClassesAndInterfaces(Class<?> clazz) {
		Set<Class<?>> result = SingletonHolder.completedSetMap.get(clazz);

		if (result == null) {
			result = new HashSet<Class<?>>();

			putSuperClassesAndInterfacesToSet(clazz, result);

			SingletonHolder.completedSetMap.put(clazz, result);
		}

		return result;
	}

	/**
	 * 将给定类型的所有父类和实现的接口类型集合放入给定的Set对象中。
	 * 
	 * @param clazz
	 *            类型对象
	 * @param classSet
	 *            Set对象
	 */
	protected static void putSuperClassesAndInterfacesToSet(Class<?> clazz, Set<Class<?>> classSet) {
		if (clazz == null) {
			return;
		}

		classSet.add(clazz);

		Class<?>[] ints = clazz.getInterfaces();
		for (int i = 0; i < ints.length; ++i) {
			putSuperClassesAndInterfacesToSet(ints[i], classSet);
		}

		if (!clazz.isInterface()) {
			putSuperClassesAndInterfacesToSet(clazz.getSuperclass(), classSet);
		}
	}

	/**
	 * 获取给定字段在给定类型中的字段类型。
	 * 
	 * @user : WuZheng
	 * @date : 2012-9-12
	 * @param clazz
	 *            要判断的类型
	 * @param fieldName
	 *            字段名称
	 * @return 如果能获取到给定字段的字段类型，则返回对应的类型对象，否则返回null。
	 */
	public static Class<?> parseFieldClass(Class<?> clazz, String fieldName) {
		try {
			Field field = clazz.getField(fieldName);

			if (field != null) {
				return field.getDeclaringClass();
			}
		} catch (Exception e) {
		}

		try {
			Method getMethod = parseFieldGetMethod(clazz, fieldName);

			return getMethod.getReturnType();
		} catch (Exception e) {
		}

		return null;
	}

	/**
	 * 获取给定类型对象中给定字段的get方法。
	 * 
	 * @param clazz
	 *            类型对象
	 * @param fieldName
	 *            字段名称
	 * @return 给定字段的get方法。
	 * @throws NoSuchMethodException
	 *             如果找不到给定字段的get方法，则抛出该异常。
	 */
	public static Method parseFieldGetMethod(Class<?> clazz, String fieldName) throws NoSuchMethodException {
		return clazz.getDeclaredMethod(parseFieldGetMethodName(fieldName));
	}

	/**
	 * 获取给定类型对象中给定字段的get方法。
	 * 
	 * @param clazz
	 *            类型对象
	 * @param fieldName
	 *            字段名称
	 * @return 给定字段的get方法。
	 * @throws NoSuchMethodException
	 *             如果找不到给定字段的get方法，则抛出该异常。
	 */
	public static Method parseFieldGetMethod(Class<?> clazz, String fieldName, Class<?> fieldType)
			throws NoSuchMethodException {
		String formattedFieldName = StringUtil.replaceWithUpperCase(fieldName, 0);
		try {
			return clazz.getDeclaredMethod("get" + formattedFieldName);
		} catch (NoSuchMethodException e) {
			if (Boolean.class == fieldType || Boolean.TYPE == fieldType) {
				return clazz.getDeclaredMethod("is" + formattedFieldName);
			} else {
				throw e;
			}
		}
	}

	/**
	 * 获取给定字段的get方法名称。
	 * 
	 * @param fieldName
	 *            字段名称
	 * @return 给定字段的get方法名称。
	 */
	public static String parseFieldGetMethodName(String fieldName) {
		return appendPrefixToMethodName(fieldName, "get");
	}

	/**
	 * 在方法名称前添加给定的前缀。
	 * 
	 * @param methodName
	 *            方法名称
	 * @param prefix
	 *            要添加的前缀
	 * @return 添加了前缀的方法名。
	 */
	protected static String appendPrefixToMethodName(String methodName, String prefix) {
		StringBuilder builder = new StringBuilder(methodName);

		builder = StringUtil.replaceWithUpperCase(builder, 0);
		builder.insert(0, prefix);

		return builder.toString();
	}

	/**
	 * 设置对象的给定字段为给定的值。
	 * 
	 * @param obj
	 *            要设置字段值的对象
	 * @param fieldName
	 *            字段名称
	 * @param fieldClass
	 *            字段类型
	 * @param fieldValue
	 *            要设置的字段值
	 * @return 设置了字段值的对象。
	 */
	public static <T> T setFieldValue(T obj, String fieldName, Class<?> fieldClass, Object fieldValue) {
		try {
			Method setMethod = parseFieldSetMethod(obj.getClass(), fieldName, fieldClass);

			setMethod.invoke(obj, fieldValue);
		} catch (Exception e) {
		}

		return obj;
	}

	/**
	 * 获取给定类型对象中给定字段的set方法。
	 * 
	 * @param clazz
	 *            类型对象
	 * @param fieldName
	 *            字段名称
	 * @param fieldClass
	 *            字段类型
	 * @return 给定字段的set方法。
	 * @throws NoSuchMethodException
	 *             如果找不到给定字段的set方法，则抛出该异常。
	 */
	public static Method parseFieldSetMethod(Class<?> clazz, String fieldName, Class<?> fieldClass)
			throws NoSuchMethodException {
		return clazz.getMethod(parseFieldSetMethodName(fieldName), fieldClass);
	}

	/**
	 * 获取给定字段的set方法名称。
	 * 
	 * @param fieldName
	 *            字段名称
	 * @return 给定字段的set方法名称。
	 */
	public static String parseFieldSetMethodName(String fieldName) {
		return appendPrefixToMethodName(fieldName, "set");
	}

	/**
	 * 返回给定基本类型的封装类型。
	 * 
	 * @param clazz
	 *            基本类型
	 * @return 给定基本类型的封装类型，如果给定的类型不是基本类型，则返回其本身。
	 * @throws NullPointerException
	 *             如果传入的<code>Class</code>对象为null。
	 */
	public static Class<?> primitiveClassToWrapClass(Class<?> clazz) {
		if (clazz.isPrimitive()) {
			Class<?> wrapClass = clazz;
			if (Boolean.TYPE == clazz) {
				wrapClass = Boolean.class;
			} else if (Byte.TYPE == clazz) {
				wrapClass = Byte.class;
			} else if (Short.TYPE == clazz) {
				wrapClass = Short.class;
			} else if (Integer.TYPE == clazz) {
				wrapClass = Integer.class;
			} else if (Long.TYPE == clazz) {
				wrapClass = Long.class;
			} else if (Float.TYPE == clazz) {
				wrapClass = Float.class;
			} else if (Double.TYPE == clazz) {
				wrapClass = Double.class;
			} else if (Character.TYPE == clazz) {
				wrapClass = Character.class;
			}

			return wrapClass;
		} else {
			return clazz;
		}
	}

}
